package com.ficus.road.maintain.controller;

import cn.hutool.core.io.IoUtil;
import com.ficus.road.maintain.authority.service.RoleService;
import com.ficus.road.maintain.authority.service.UserService;
import com.ficus.road.maintain.core.exception.BadRequestException;
import com.ficus.road.maintain.core.model.*;
import com.ficus.road.maintain.core.model.coverage.GpsAmendmentFilter;
import com.ficus.road.maintain.core.model.coverage.GpsAmendmentRoadFilter;
import com.ficus.road.maintain.dao.CarDao;
import com.ficus.road.maintain.dao.CompanyDao;
import com.ficus.road.maintain.dao.GpsHistoryDao;
import com.ficus.road.maintain.dao.RoadInfoDao;
import com.ficus.road.maintain.service.CameraService;
import com.ficus.road.maintain.service.CarService;
import com.ficus.road.maintain.service.GpsHistoryService;
import com.ficus.road.maintain.util.LngLonUtil;
import com.ficus.road.maintain.util.MyDateUtil;
import com.ficus.road.maintain.util.SpringUtil;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.*;

@RestController
@Slf4j
public class CarController {
    private static String path = "/home/xianwen.yang/Desktop";
    @Autowired
    private CarService carService;
    @Autowired
    private CameraService cameraService;
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private GpsHistoryService gpsHistoryService;
    @Autowired
    private GpsHistoryDao gpsHistoryDao;
    @Autowired
    private RoadInfoDao roadInfoDao;
    @Autowired
    private CompanyDao companyDao;
    @Autowired
    private CarDao carDao;
    @Autowired
    private SpringUtil springUtil;
    @Value("${coverage.gps_fix_distance}")
    private Integer gpsFixDistance;

    @PostMapping("/gps/generate/{unitId}/{roadName}")
    public ResponseEntity<String> generateGps(@PathVariable String unitId, @PathVariable String roadName) throws FileNotFoundException {
        List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("unit_id").is(unitId)));
        for (RoadInfo roadInfo : roadInfoList) {
            if (!Objects.equal(roadInfo.getRoadName(), roadName)) {
                continue;
            }
            List<Coordinate> coordinates = roadInfo.getCoordinates();
            if (CollectionUtils.isEmpty(coordinates)) {
                log.warn("标段: {}, 路名: {} 缺少坐标信息", unitId, roadInfo.getRoadName());
                continue;
            }
            System.out.println("----------");
            System.out.println("路名:" + roadInfo.getRoadName());
            System.out.println("采样前数据量: " + coordinates.size());
            String beforePath = path + "/" + unitId + "_before";
            String beforeString = generateGpsString(coordinates);
            IoUtil.writeUtf8(new FileOutputStream(beforePath), true, beforeString);
            coordinates = LngLonUtil.gpsSampling(coordinates, 50);
            String afterString = generateGpsString(coordinates);
            String afterPath = path + "/" + unitId + "_after";
            IoUtil.writeUtf8(new FileOutputStream(afterPath), true, afterString);
            System.out.println("采样后数据量: " + coordinates.size());
        }
        return ResponseEntity.ok("success");
    }

    private String generateGpsString(List<Coordinate> coordinates) {
        String format = "[%s, %s]";
        List<String> res = new ArrayList<>();
        Joiner joiner = Joiner.on(",");
        coordinates.forEach(coordinate -> {
            String s = String.format(format, coordinate.getY(), coordinate.getX());
            res.add(s);
        });
        return joiner.join(res);
    }

    @RequestMapping(value = "/car/query",
            produces = {"application/json"},
            method = RequestMethod.GET)
    // todo: 前端轮询
//    @PreAuthorize("@rm.check('CAR')")
    public ResponseEntity<List<CarVO>> search(@RequestParam(required = false) String source) {
        Query query = new Query();
        List<CarVO> cars = carService.queryCar(query, source);
        return new ResponseEntity<>(cars, HttpStatus.OK);
    }

    /**
     * 车辆版本上报
     *
     * @return
     */
    @RequestMapping(value = "/car/version",
            method = RequestMethod.GET)
    public ResponseEntity<IdResult> reportVersion(@RequestParam("carId") String carId, @RequestParam("version") String version) {
        carService.reportVersion(carId, version);
        IdResult idResult = new IdResult();
        idResult.setId(carId);
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }

    //查询车辆详情
    @RequestMapping(value = "/car/{id}",
            method = RequestMethod.GET)
//    @PreAuthorize("@rm.check('CAR')")
    public ResponseEntity<CarVO> getCarInfo(@PathVariable("id") String id, @RequestParam(required = false) String source) {
        CarVO carVO = carService.getCar(id, source);
        return new ResponseEntity<>(carVO, HttpStatus.OK);
    }

    //更新车辆信息
    @RequestMapping(value = "/car/{id}",
            produces = {"application/json"},
            method = RequestMethod.PATCH)
    public ResponseEntity<IdResult> updateCar(@PathVariable("id") String id, @RequestBody CarDTO carDTO) {
        carService.updateCar(id, carDTO);
        IdResult idResult = new IdResult();
        idResult.setId(id);
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }

    @RequestMapping(value = "/camera/query",
            produces = {"application/json"},
            method = RequestMethod.GET)
//    @PreAuthorize("@rm.check('CAR')")
    public ResponseEntity<List<Camera>> search(@Min(0) @Valid @RequestParam(defaultValue = "0") Integer start,
                                               @Min(1) @Valid @RequestParam(defaultValue = "10000") Integer limit,
                                               String carId) {
        Query query;
        if (StringUtils.isEmpty(carId)) {
            query = new Query();
        } else {
            Criteria criteria = Criteria.where("car_id").is(carId);
            query = new Query(criteria);
        }
        query.skip(start).limit(limit);
        List<Camera> cameras = cameraService.queryCamera(query);
        query.fields().include("_id");
        List<Camera> totalList = cameraService.queryCamera(query);
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        Boolean isFinal = Boolean.FALSE;
        if (cameras.size() != limit) {
            isFinal = Boolean.TRUE;
        }
        headers.add("X-OneAPI-Exhausted", String.valueOf(isFinal));
        headers.add("X-OneAPI-EstimatedSize", String.valueOf(totalList.size()));
        return new ResponseEntity<>(cameras, headers, HttpStatus.OK);
    }

    @PostMapping("/car/out/list")
    public ResponseEntity<List<CarOutDTO>> getCarOutInfo(@RequestBody CarOutQuery carOutQuery) {
        if (ObjectUtils.isEmpty(carOutQuery) || StringUtils.isEmpty(carOutQuery.getDate()) || ObjectUtils.isEmpty(carOutQuery.getStatus())) {
            throw new BadRequestException("bad request exception, date or status cannot be null!");
        }
        List<CarOutDTO> carOutInfo = carService.getCarOutInfo(carOutQuery);
        return new ResponseEntity<>(carOutInfo, HttpStatus.OK);
    }

    @GetMapping("/car_manager/car/maintenance/{carId}")
    public ResponseEntity<Boolean> carMaintenance(@PathVariable("carId") String carId) {
        carService.carMaintenance(carId);
        return new ResponseEntity<>(Boolean.TRUE, HttpStatus.OK);
    }

    @DeleteMapping("/car/gps/amendment")
    public ResponseEntity<String> gpsDelete(@RequestBody GpsAmendmentFilter filter) {
        List<GpsHistory> byQuery = gpsHistoryDao.findByQuery(new Query(Criteria.where("car_id").is(filter.getCarId()).and("is_supplement").is(Boolean.TRUE)));
        byQuery.forEach(gps -> {
            gpsHistoryDao.deleteById(gps.getId());
        });
        return new ResponseEntity<>("删除补录数据成功", HttpStatus.OK);
    }

    /**
     * 为绿景观轨迹补录
     *
     * @param filter
     * @return
     */
    @PostMapping("/car/gps/amendment/wljg")
    public ResponseEntity<String> gpsAmendment2(@RequestBody GpsAmendmentFilter filter) {
        long timestamp = new Date().getTime() / 1000;

        List<String> unitIdList = filter.getUnitIdList();
        for (String unitId : unitIdList) {
            List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("unit_id").is(unitId)));
            for (RoadInfo roadInfo : roadInfoList) {
                List<Coordinate> coordinates = roadInfo.getCoordinates();
                if (CollectionUtils.isEmpty(coordinates)) {
                    log.warn("标段: {}, 路名: {} 缺少坐标信息", unitId, roadInfo.getRoadName());
                    continue;
                }
                System.out.println("----------");
                System.out.println("路名:" + roadInfo.getRoadName());
                System.out.println("采样前数据量: " + coordinates.size());
                coordinates = LngLonUtil.gpsSampling(coordinates, gpsFixDistance);
                System.out.println("采样后数据量: " + coordinates.size());
                for (Coordinate coordinate : coordinates) {
                    int speed = RandomUtils.nextInt(10, 70);
                    double longitude = Double.parseDouble(coordinate.getX());
                    double latitude = Double.parseDouble(coordinate.getY());
                    GpsHistory gpsHistory = GpsHistory.builder().carId(filter.getCarId()).timestamp(timestamp).longitude(longitude)
                            .latitude(latitude).speed(speed).location(Arrays.asList(longitude, latitude)).isSupplement(Boolean.TRUE)
                            .creationTime(new Date(timestamp * 1000)).id(filter.getCarId() + timestamp).build();
                    gpsHistoryDao.saveOrUpdate(gpsHistory);
                    timestamp = timestamp + 2;
                }
            }
        }
        return new ResponseEntity<>("补录成功", HttpStatus.OK);
    }


    /**
     * 按照标段补录轨迹
     *
     * @param filter
     * @return
     */
    @PostMapping("/car/gps/amendment")
    public ResponseEntity<String> gpsAmendment(@RequestBody GpsAmendmentFilter filter) {
        Date today = new Date();
        long startOfTimestamp = MyDateUtil.getStartOfDay(today).getTime() / 1000;
//        TypedAggregation<Company> units = Aggregation.newAggregation(Company.class, Aggregation.unwind("units"));
//        List<Company> aggregate = companyDao.aggregate(units);
//        aggregate.stream().filter(item -> item.getUnits().contains(filter.getUnitIdList()))
//        if (ObjectUtils.isEmpty(aggregate)) {
//            return new ResponseEntity<>("")
//        }
        Car car = carDao.findOne(new Query(Criteria.where("handle_company").is(filter.getCompanyId())).skip(0).limit(1));
        if (ObjectUtils.isEmpty(car)) {
            return new ResponseEntity<>("参数有误, 找不到公司的车辆信息", HttpStatus.BAD_REQUEST);
        }
        if (StringUtils.isNotEmpty(filter.getCarId())) {
            car.setId(filter.getCarId());
        }
        Criteria criteria = Criteria.where("car_id").is(filter.getCarId());
        if (StringUtils.isNotEmpty(filter.getDate())) {
            Date date = MyDateUtil.strParseDate(filter.getDate(), "yyyy-MM-dd");
            Date startOfDay = MyDateUtil.getStartOfDay(date);
            Date endOfDay = MyDateUtil.getEndOfDay(date);
            startOfTimestamp = startOfDay.getTime() / 1000;
            criteria.and("timestamp").lte(endOfDay.getTime() / 1000).gt(startOfTimestamp);
        } else {
            criteria.and("timestamp").gte(startOfTimestamp);
        }
        Query gpsQuery = new Query(criteria);
        gpsQuery.with(new Sort(Sort.Direction.DESC, "timestamp")).skip(0).limit(1);
        GpsHistory lastOne = gpsHistoryDao.findOne(gpsQuery);
//        if (ObjectUtils.isNotEmpty(lastOne) && ObjectUtils.isNotEmpty(lastOne.getIsSupplement())) {
//            return new ResponseEntity<>("该车辆已经补录", HttpStatus.OK);
//        }
        if (filter.getIsRealTime()) {
            lastOne = GpsHistory.builder().timestamp(new Date().getTime() / 1000).build();
        } else if (ObjectUtils.isEmpty(lastOne)) {
            Date date = null;
            if (StringUtils.isEmpty(filter.getDate())) {
                date = new Date();
            } else {
                date = MyDateUtil.strParseDate(filter.getDate(), "yyyy-MM-dd HH:mm:ss");
            }
            lastOne = GpsHistory.builder().timestamp(date.getTime() / 1000).build();
//            return new ResponseEntity<>("参数有误, 找不到车辆当天出车信息", HttpStatus.BAD_REQUEST);
        }
//        if (ObjectUtils.isEmpty(lastOne)) {
//            MyDateUtil.strParseDate("")
//            lastOne = GpsHistory.builder().timestamp()
//        }
        Long timestamp = lastOne.getTimestamp() + 2;
        List<String> unitIdList = filter.getUnitIdList();
        if (CollectionUtils.isEmpty(unitIdList)) {
            Company company = companyDao.fetch(filter.getCompanyId());
            if (ObjectUtils.isEmpty(company)) {
                return new ResponseEntity<>("参数有误, 找不到公司信息", HttpStatus.BAD_REQUEST);
            }
            unitIdList = new ArrayList<>(company.getUnits());
        }
        for (String unitId : unitIdList) {
            List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("unit_id").is(unitId)));
            for (RoadInfo roadInfo : roadInfoList) {
                List<Coordinate> coordinates = roadInfo.getCoordinates();
                if (CollectionUtils.isEmpty(coordinates)) {
                    log.warn("标段: {}, 路名: {} 缺少坐标信息", unitId, roadInfo.getRoadName());
                    continue;
                }
                System.out.println("----------");
                System.out.println("路名:" + roadInfo.getRoadName());
                System.out.println("采样前数据量: " + coordinates.size());
                coordinates = LngLonUtil.gpsSampling(coordinates, gpsFixDistance);
                System.out.println("采样后数据量: " + coordinates.size());
                for (Coordinate coordinate : coordinates) {
                    int speed = RandomUtils.nextInt(10, 70);
                    double longitude = Double.parseDouble(coordinate.getX());
                    double latitude = Double.parseDouble(coordinate.getY());
                    GpsHistory gpsHistory = GpsHistory.builder().carId(filter.getCarId()).timestamp(timestamp).longitude(longitude)
                            .latitude(latitude).speed(speed).location(Arrays.asList(longitude, latitude)).isSupplement(Boolean.TRUE)
                            .creationTime(new Date(timestamp * 1000)).id(filter.getCarId() + timestamp).build();
                    gpsHistoryDao.saveOrUpdate(gpsHistory);
                    timestamp = timestamp + 2;
                }
            }
        }
        return new ResponseEntity<>("补录成功", HttpStatus.OK);
    }

    @PostMapping("/car/gps/amendmentByDate")
    @PreAuthorize("@rm.check()")
    public ResponseEntity<String> gpsAmendmentByDate(@RequestBody GpsAmendmentFilter filter) {
        Date startDate = MyDateUtil.strParseDate(filter.getDate(), "yyyy-MM-dd HH:mm:ss");
        Date startOfDay = MyDateUtil.getStartOfDay(startDate);
        Date endOfDay = DateUtils.addDays(startOfDay, 1);
        List<GpsHistory> gpsHistories = gpsHistoryDao.findByQuery(new Query(Criteria.where("car_id").is(filter.getCarId())
                .and("timestamp").gte(startOfDay.getTime() / 1000).lte(endOfDay.getTime() / 1000)));
        gpsHistories.forEach(gps -> {
            gpsHistoryDao.deleteById(gps.getId());
        });
        log.info("删除历史记录完成, 删除数量: {}", gpsHistories.size());
        long timestamp = startDate.getTime() / 1000;
        List<String> unitIdList = filter.getUnitIdList();
        if (CollectionUtils.isEmpty(unitIdList)) {
            Company company = companyDao.fetch(filter.getCompanyId());
            if (ObjectUtils.isEmpty(company)) {
                return new ResponseEntity<>("参数有误, 找不到公司信息", HttpStatus.BAD_REQUEST);
            }
            unitIdList = new ArrayList<>(company.getUnits());
        }
        for (String unitId : unitIdList) {
            List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("unit_id").is(unitId)));
            for (RoadInfo roadInfo : roadInfoList) {
                List<Coordinate> coordinates = roadInfo.getCoordinates();
                if (CollectionUtils.isEmpty(coordinates)) {
                    log.warn("标段: {}, 路名: {} 缺少坐标信息", unitId, roadInfo.getRoadName());
                    continue;
                }
                System.out.println("----------");
                System.out.println("路名:" + roadInfo.getRoadName());
                System.out.println("采样前数据量: " + coordinates.size());
                coordinates = LngLonUtil.gpsSampling(coordinates, gpsFixDistance);
                System.out.println("采样后数据量: " + coordinates.size());
                for (Coordinate coordinate : coordinates) {
                    int speed = RandomUtils.nextInt(10, 70);
                    double longitude = Double.parseDouble(coordinate.getX());
                    double latitude = Double.parseDouble(coordinate.getY());
                    GpsHistory gpsHistory = GpsHistory.builder().carId(filter.getCarId()).timestamp(timestamp).longitude(longitude)
                            .latitude(latitude).speed(speed).location(Arrays.asList(longitude, latitude)).isSupplement(Boolean.TRUE)
                            .creationTime(new Date(timestamp * 1000)).id(filter.getCarId() + timestamp).build();
                    gpsHistoryDao.saveOrUpdate(gpsHistory);
                    timestamp = timestamp + 2;
                }
            }
        }
        return new ResponseEntity<>("补录成功", HttpStatus.OK);
    }


    /**
     * 按照标段和路名补录轨迹
     *
     * @return
     */
    @PostMapping("/car/gps/amendment/road")
    public ResponseEntity<String> gpsAmendmentByRoad(@RequestBody GpsAmendmentRoadFilter filter) {
        Date today = new Date();
        long startOfTimestamp = MyDateUtil.getStartOfDay(today).getTime() / 1000;
        Car car = carDao.findOne(new Query(Criteria.where("handle_company").is(filter.getCompanyId())).skip(0).limit(1));
//        if (ObjectUtils.isEmpty(car)) {
//            return new ResponseEntity<>("参数有误, 找不到公司的车辆信息", HttpStatus.BAD_REQUEST);
//        }
        if (StringUtils.isNotEmpty(filter.getCarId())) {
            car.setId(filter.getCarId());
        }
        Criteria criteria = Criteria.where("car_id").is(car.getId());
        if (StringUtils.isNotEmpty(filter.getDate())) {
            Date date = MyDateUtil.strParseDate(filter.getDate(), "yyyy-MM-dd");
            Date startOfDay = MyDateUtil.getStartOfDay(date);
            Date endOfDay = MyDateUtil.getEndOfDay(date);
            startOfTimestamp = startOfDay.getTime() / 1000;
            criteria.and("timestamp").lte(endOfDay.getTime() / 1000).gt(startOfTimestamp);
        } else {
            criteria.and("timestamp").gte(startOfTimestamp);
        }
        Query gpsQuery = new Query(criteria);
        gpsQuery.with(new Sort(Sort.Direction.DESC, "timestamp")).skip(0).limit(1);
        GpsHistory lastOne = gpsHistoryDao.findOne(gpsQuery);
        if (filter.getIsRealTime()) {
            lastOne = GpsHistory.builder().timestamp(new Date().getTime() / 1000).build();
        } else if (ObjectUtils.isEmpty(lastOne)) {
            Date date = null;
            if (StringUtils.isEmpty(filter.getDate())) {
                date = new Date();
            } else {
                date = MyDateUtil.strParseDate(filter.getDate(), "yyyy-MM-dd HH:mm:ss");
            }
            lastOne = GpsHistory.builder().timestamp(date.getTime() / 1000).build();
        }
        Long timestamp = lastOne.getTimestamp() + 2;
        Set<String> unitIdList = filter.getUnitRoadNameMap().keySet();
        for (String unitId : unitIdList) {
            List<String> realRoadNameList = filter.getUnitRoadNameMap().get(unitId);
            List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("unit_id").is(unitId).and("road_name").in(realRoadNameList)));
            for (RoadInfo roadInfo : roadInfoList) {
                List<Coordinate> coordinates = roadInfo.getCoordinates();
                if (CollectionUtils.isEmpty(coordinates)) {
                    log.warn("标段: {}, 路名: {} 缺少坐标信息", unitId, roadInfo.getRoadName());
                    continue;
                }
                System.out.println("采样前数据量: " + coordinates.size());
                coordinates = LngLonUtil.gpsSampling(coordinates, 50);
                System.out.println("采样后数据量: " + coordinates.size());
                for (Coordinate coordinate : coordinates) {
                    double longitude = Double.parseDouble(coordinate.getX());
                    double latitude = Double.parseDouble(coordinate.getY());
                    GpsHistory gpsHistory = GpsHistory.builder().carId(car.getId()).timestamp(timestamp).longitude(longitude)
                            .latitude(latitude).location(Arrays.asList(longitude, latitude)).isSupplement(Boolean.TRUE)
                            .creationTime(new Date(timestamp * 1000)).id(car.getId() + timestamp).build();
                    gpsHistoryDao.saveOrUpdate(gpsHistory);
                    timestamp = timestamp + 2;
                }
            }
        }
        return new ResponseEntity<>("补录成功", HttpStatus.OK);
    }

    @PostMapping("/car/gps/bugFix")
    public ResponseEntity<String> gpsAmendment() {
//        ApplicationContext applicationContext = springUtil.getApplicationContext();
        List<GpsHistory> is_fixed = gpsHistoryDao.findByQuery(new Query(Criteria.where("is_fixed").is(Boolean.FALSE)));
        is_fixed.parallelStream().forEach(item -> {
            item.setIsFixed(Boolean.TRUE);
            gpsHistoryDao.saveOrUpdate(item);
        });
        return new ResponseEntity<>("success", HttpStatus.OK);
    }

    /**
     * 指定标段和road补录gps
     *
     * @return
     */
    @GetMapping("/car/gps/amendment/road")
    public ResponseEntity<String> gpsAmendmentRoad(@RequestBody GpsAmendmentRoadFilter filter) {
        Car car = carDao.findOne(new Query(Criteria.where("handle_company").is(filter.getCompanyId())).skip(0).limit(1));
        if (ObjectUtils.isEmpty(car)) {
            return new ResponseEntity<>("参数有误, 找不到公司的车辆信息", HttpStatus.BAD_REQUEST);
        }

        return new ResponseEntity<>("success", HttpStatus.OK);
    }


    @GetMapping("/car/flushUnits")
    public ResponseEntity<String> flushUnits() {
        carDao.findAll().forEach(car -> {
            Company company = companyDao.fetch(car.getHandleCompany());
            car.setUnits(new ArrayList<>(company.getUnits()));
            carDao.saveOrUpdate(car);
        });
        return new ResponseEntity<>("success", HttpStatus.OK);
    }
}
